<script lang="ts" setup>
import * as d3 from 'd3'
import moment from 'moment'

interface SaleData {
  date: Date
  value: number
}

const props = withDefaults(defineProps<{
  data: SaleData[]
  color?: string
}>(), {
  color: 'steelblue',
})

const emits = defineEmits<{
  (e: 'date', ds: string): void
}>()

function goDate(d: Date) {
  emits('date', moment(d).format('YYYY-MM-DD'))
}

const svgContainer = ref(null as HTMLElement | null)

function draw(data: SaleData[]) {
  // SVG属性
  const width = 350
  const height = 200
  const marginLeft = 10
  const marginRight = 10
  const marginTop = 30
  const marginBottom = 25
  const svg = d3.create('svg')
    .attr('width', width)
    .attr('height', height)
    .attr('viewBox', [0, 0, width, height])
    .attr('style', 'max-width: 100%; height: auto; height: intrinsic;')

  // x轴
  const d0 = props.data[0].date
  const d1 = new Date(props.data[props.data.length - 1].date.valueOf() + 86400000)
  const xDomain = d3.utcDay.range(d0, d1)
  const xRange = [marginLeft, width - marginRight]
  const x = d3.scaleBand(xDomain, xRange)
  // console.log(`2023-09-19: ${x(new Date('2023-09-19'))}`)

  const strangeOffset = (width - marginLeft - marginRight) / props.data.length / 2

  svg.append('g')
    .attr('transform', `translate(0,${height - marginBottom})`)
    .style('font-size', '16px')
    .call(
      d3.axisBottom(x)
        .tickFormat((date, _) => moment(date).format('D')),
    )

  // y轴
  const yDomain = [0, d3.max(props.data, d => d.value) as number]
  const yRange = [height - marginBottom, marginTop]
  const y = d3.scaleLinear(yDomain, yRange).nice()
  svg.append('g')
    .attr('transform', `translate(${marginLeft},0)`)
    .call(
      d3.axisLeft(y)
        .ticks(height / 40)
        .tickFormat((_date, _index) => ''),
    )
    .call(g => g.select('.domain').remove())
    .call(g => g.selectAll('.tick line')
      .attr('x2', width - marginLeft - marginRight)
      .attr('stroke-opacity', 0.1))

  // 折线
  const line = d3.line<SaleData>(
    d => x(d.date) ?? 0,
    d => y(d.value),
  )
  svg.append('path')
    .attr('transform', `translate(${strangeOffset},0)`)
    .attr('fill', 'none')
    .attr('stroke', props.color)
    .attr('stroke-width', 3)
    .attr('d', line(props.data))

  // 画点: 点
  svg.append('g')
    .attr('transform', `translate(${strangeOffset},0)`)
    .attr('stroke', props.color)
    .attr('stroke-width', 1.5)
    .attr('fill', props.color)
    .selectAll('circle')
    .data(props.data)
    .join('circle')
    .attr('cx', d => x(d.date) ?? 0)
    .attr('cy', d => y(d.value))
    .attr('r', 5)

  // 画点: 虚线
  svg.append('g')
    .attr('transform', `translate(${strangeOffset},0)`)
    .attr('stroke', props.color)
    .attr('stroke-width', 1.5)
    .attr('fill', props.color)
    .selectAll('line')
    .data(props.data)
    .join('line')
    .attr('stroke-dasharray', '3 2')
    .attr('x1', d => x(d.date) ?? 0)
    .attr('y1', height - marginBottom)
    .attr('x2', d => x(d.date) ?? 0)
    .attr('y2', d => y(d.value))

  // 画点: 金额
  svg.append('g')
    .attr('transform', `translate(${strangeOffset},0)`)
    .attr('font-family', 'sans-serif')
    // .attr('font-size', 12)
    .selectAll('text')
    .data(props.data)
    .join('text')
    // .attr('dy', '0.35em')
    .attr('text-anchor', 'middle')
    .attr('x', d => x(d.date))
    .attr('y', d => y(d.value) - 10)
    // .text(d => `${moment(d.date).format('MM/DD')} ${d.value}`)
    .text(d => `${d.value}`)

  svg
    .on('click', click)

  if (svgContainer.value) {
    svgContainer.value.innerHTML = ''
    svgContainer.value.appendChild(
      svg.node() as SVGSVGElement,
    )
  }

  const points = data.map(d => [x(d.date), y(d.value)])

  function click(event: any) {
    let [xm, ym] = d3.pointer(event)
    xm -= strangeOffset
    const i = d3.leastIndex(points.map(d => [d[0], 0]), ([x, y]) => Math.hypot(x - xm, y - 0)) as number
    // const [x, y] = points[i]
    goDate(data[i].date)

    // path.style('stroke', ({ z }) => z === k ? null : '#ddd').filter(({ z }) => z === k).raise()
    // dot.attr('transform', `translate(${x},${y})`)
    // dot.select('text').text(k)
    // svg.property('value', unemployment[i]).dispatch('input', { bubbles: true })
  }
}

watchEffect(() => {
  if (props.data.length) {
    // console.log('draw', props.data)

    draw(props.data)
  }
})
</script>

<template>
  <div ref="svgContainer" />
</template>

<style scoped>

</style>
